package rewards.internal;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import rewards.AccountContribution;
import rewards.Dining;
import rewards.RewardConfirmation;
import rewards.RewardNetwork;
import rewards.RewardNetworkException;
import rewards.domain.account.Account;
import rewards.domain.account.AccountRepository;
import rewards.domain.restaurant.Restaurant;
import rewards.domain.restaurant.RestaurantRepository;
import rewards.domain.reward.RewardRepository;
import common.money.MonetaryAmount;

/**
 * Rewards an Account for Dining at a Restaurant.
 * 
 * The sole Reward Network implementation. This object is an application-layer
 * service responsible for coordinating with the domain-layer to carry out the
 * process of rewarding benefits to accounts for dining.
 * 
 * In other words, this class implements the "reward account for dining" use
 * case.
 */
@Service("rewardNetwork")
public class RewardNetworkImpl implements RewardNetwork {

	private final AccountRepository accountRepository;

	private final RestaurantRepository restaurantRepository;

	private final RewardRepository rewardRepository;

	/**
	 * Creates a new reward network.
	 * 
	 * @param accountRepository
	 *            the repository for loading accounts to reward
	 * @param restaurantRepository
	 *            the repository for loading restaurants that determine how much
	 *            to reward
	 * @param rewardRepository
	 *            the repository for recording a record of successful reward
	 *            transactions
	 */
	@Autowired
	public RewardNetworkImpl(AccountRepository accountRepository,
			RestaurantRepository restaurantRepository,
			RewardRepository rewardRepository) {
		this.accountRepository = accountRepository;
		this.restaurantRepository = restaurantRepository;
		this.rewardRepository = rewardRepository;
	}

	@Override
	@Transactional
	public RewardConfirmation rewardAccountFor(Dining dining)
			throws RewardNetworkException {
		Account account = accountRepository.findByCreditCard(dining
				.getCreditCardNumber());
		Restaurant restaurant = restaurantRepository
				.findByMerchantNumber(dining.getMerchantNumber());
		MonetaryAmount amount = restaurant.calculateBenefitFor(account, dining);
		AccountContribution contribution = account.makeContribution(amount);
		accountRepository.updateBeneficiaries(account);
		return rewardRepository.confirmReward(contribution, dining);
	}

}
